package public

import (
	"fmt"
	"sync/atomic"
	"time"

	"go_gateway/golang_common/lib"

	"github.com/garyburd/redigo/redis"
)

// Redis 流量计数器
type RedisFlowCountService struct {
	AppID       string        // 计数器标识（如：flow_total, flow_service_xxx）
	Interval    time.Duration // 统计间隔（1秒）
	QPS         int64         // 当前QPS
	Unix        int64         // 上次计算时间戳
	TickerCount int64         // 当前时间窗口内的计数（原子操作）
	TotalCount  int64         // 总请求数
}

func NewRedisFlowCountService(appID string, interval time.Duration) *RedisFlowCountService {
	reqCounter := &RedisFlowCountService{
		AppID:    appID,
		Interval: interval,
		QPS:      0,
		Unix:     0,
	}
	// 启动后台定时统计任务
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println(err)
			}
		}()
		ticker := time.NewTicker(interval)
		for {
			<-ticker.C                                               // 等待下一个时间间隔
			tickerCount := atomic.LoadInt64(&reqCounter.TickerCount) // 读取当前计数
			atomic.StoreInt64(&reqCounter.TickerCount, 0)            // 重置计数器

			currentTime := time.Now()
			dayKey := reqCounter.GetDayKey(currentTime)   // 天级统计键
			hourKey := reqCounter.GetHourKey(currentTime) // 小时级统计键
			// 保存到 Redis 使用管道减少网络往返
			if err := RedisConfPipline(func(c redis.Conn) {
				c.Send("INCRBY", dayKey, tickerCount) // 增加天级计数
				c.Send("EXPIRE", dayKey, 86400*2)     // 设置2天过期
				c.Send("INCRBY", hourKey, tickerCount)
				c.Send("EXPIRE", hourKey, 86400*2)
			}); err != nil {
				fmt.Println("RedisConfPipline err", err)
				continue
			}

			totalCount, err := reqCounter.GetDayData(currentTime)
			if err != nil {
				fmt.Println("reqCounter.GetDayData err", err)
				continue
			}
			nowUnix := time.Now().Unix()
			if reqCounter.Unix == 0 {
				reqCounter.Unix = time.Now().Unix()
				continue
			}

			// 基于时间差计算QPS
			tickerCount = totalCount - reqCounter.TotalCount // 时间窗口内的增量
			if nowUnix > reqCounter.Unix {
				reqCounter.TotalCount = totalCount
				reqCounter.QPS = tickerCount / (nowUnix - reqCounter.Unix) // QPS = 增量 / 时间差
				reqCounter.Unix = time.Now().Unix()
			}
		}
	}()
	return reqCounter
}

// 天 键名格式
func (o *RedisFlowCountService) GetDayKey(t time.Time) string {
	dayStr := t.In(lib.TimeLocation).Format("20060102")
	return fmt.Sprintf("%s_%s_%s", RedisFlowDayKey, dayStr, o.AppID)
}

// 小时 键名格式
func (o *RedisFlowCountService) GetHourKey(t time.Time) string {
	hourStr := t.In(lib.TimeLocation).Format("2006010215")
	return fmt.Sprintf("%s_%s_%s", RedisFlowHourKey, hourStr, o.AppID)
}

func (o *RedisFlowCountService) GetHourData(t time.Time) (int64, error) {
	return redis.Int64(RedisConfDo("GET", o.GetHourKey(t)))
}

func (o *RedisFlowCountService) GetDayData(t time.Time) (int64, error) {
	return redis.Int64(RedisConfDo("GET", o.GetDayKey(t)))
}

// 原子增加计数
func (o *RedisFlowCountService) Increase() {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println(err)
			}
		}()
		atomic.AddInt64(&o.TickerCount, 1) // 使用 atomic.AddInt64 保证线程安全
	}()
}
